Read in MoTR Data
# Interesting way of reading multiple files in one directory!
file_prefix = "../reading_measures/cleaned_f160"
fnames = list.files(path=file_prefix)
# fnames: [1] "reader_55_reading_measures.csv" "reader_56_reading_measures.csv"
# [3] "reader_59_reading_measures.csv" "reader_61_reading_measures.csv"
# Read in the data
df = data.frame()
# uses the mutate() function from the dplyr package to create a new column in a data frame called subj. The new column is created by removing the substring _reading_measures.csv from the values in an existing column called f.
for (f in fnames) {
temp = read.csv(paste0(file_prefix, "/", f)) %>%
mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration,
gaze_duration, go_pass_time, FPReg, subj) %>%
rename(go_past_time = go_pass_time)
df = rbind(df, temp)
}
df
# `gather will take column 6-9's column name make it rows, name 'metric', take their value as another row`
motr_df = df %>%
mutate(expr_id = if_else(expr_id == 1, "Attachment", "Provo")) %>%
gather(metric, value, 6:10)
motr_df
NA
# Average across subjects
motr_agg_df = motr_df %>%
drop_na() %>%
group_by(expr_id, cond_id, para_nr, word, word_nr, metric) %>%
summarise(value = mean(value)) %>% # sum up four subjects for each metric and divide by 4.
ungroup() %>%
arrange(expr_id, cond_id, para_nr, word_nr) # like sort in python
`summarise()` has grouped output by 'expr_id', 'cond_id', 'para_nr', 'word', 'word_nr'. You can override using the `.groups` argument.
# View(motr_agg_df)
motr_agg_df
table(motr_df$subj)
reader_64 reader_66 reader_67 reader_68 reader_70
11990 11990 11990 11990 11990
# reader_55 reader_56 reader_59 reader_61
# 3100 3100 3100 3100
motr_provo_df = motr_agg_df %>%
filter(expr_id == "Provo") %>%
rename(text_id = para_nr,
word_text_idx = word_nr,
motr_value = value) %>%
dplyr::select(-expr_id, -cond_id)
motr_provo_df
NA
Comparison to Provo
# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("../ancillary_data/provo_df.csv") %>%
dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
rename(word_idx = trigger_idx)
provo_modeling_df
NA
# Read in Provo eyetracking data
provo_raw_df = read.csv("../ancillary_data/provo_eyetracking.csv")
provo_eyetracking_df = provo_raw_df %>%
dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, IA_ID, Word,IA_FIRST_FIXATION_DURATION, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT) %>%
rename( first_duration = IA_FIRST_FIXATION_DURATION, # whether it is first pass?
gaze_duration = IA_FIRST_RUN_DWELL_TIME,
total_duration = IA_DWELL_TIME,
go_past_time = IA_REGRESSION_PATH_DURATION,
FPReg = IA_REGRESSION_OUT,
subj = Participant_ID,
text_id = Text_ID,
sent_id = Sentence_Number,
word_idx = Word_In_Sentence_Number,
word_text_idx = IA_ID,
word = Word,
ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>% # notice:average across subj, binary(0,1) becomes float.
mutate(gaze_duration = ifelse(ff_progressive == 0, 0, gaze_duration),
go_past_time = ifelse(ff_progressive == 0, 0, go_past_time)) %>%
dplyr::select(-ff_progressive) %>%
gather(metric, value, 7:11) %>% # not include FFReg which is in column 11
mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
drop_na() %>% # actually, drop first word in a sentence
group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
summarise(value = mean(value)) %>%
ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)
provo_eyetracking_df
NA
provo_df = merge(provo_eyetracking_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
arrange(text_id, sent_id, word_idx)
provo_df
provo_df = merge(provo_df, motr_provo_df, by=c("text_id", "word_text_idx", "metric")) %>%
rename(eyetr_value = value) %>%
arrange(text_id, sent_id, word_idx) %>%
filter(word.x == word) %>% #word.y has no captical word
dplyr::select(-word.x, -word.y) %>%
mutate(motr_outlier = if_else(motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
filter(motr_outlier == F) %>% # clear outlier -> 13 was filtered.
gather(measure, value, c("eyetr_value", "motr_value")) %>%
# filter(metric != "first_duration") %>%
filter(metric != "FPReg")
# View(provo_df)
provo_df %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
facet_wrap(.~measure) +
xlab("Reading Measure")
Error in `combine_vars()`:
! Faceting variables must have at least one value
Backtrace:
1. base (local) `<fn>`(x)
2. ggplot2:::print.ggplot(x)
4. ggplot2:::ggplot_build.ggplot(x)
5. layout$setup(data, plot$data, plot$plot_env)
6. ggplot2 (local) setup(..., self = self)
7. self$facet$compute_layout(data, self$facet_params)
8. ggplot2 (local) compute_layout(..., self = self)
9. ggplot2::combine_vars(data, params$plot_env, vars, drop = params$drop)

provo_df %>%
filter(measure == "motr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for motr")

provo_df %>%
filter(measure == "eyetr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for eyetr")

gd_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
# View(gd_df)
cor.test(gd_df$eyetr_value, gd_df$motr_value) #cor rise from 0.529 to 0.5989
Pearson's product-moment correlation
data: gd_df$eyetr_value and gd_df$motr_value
t = 18, df = 377, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6113 0.7226
sample estimates:
cor
0.6707
provo_df %>%
spread(measure, value) %>%
ggplot(aes(x = motr_value, y=eyetr_value, color=metric)) +
geom_point(alpha = 0.2) +
facet_wrap(.~metric, scales="free") +
geom_smooth()

# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)
gd_df_2 = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
cor.test(gd_df_2$eyetr_value, gd_df_2$motr_value)
Pearson's product-moment correlation
data: gd_df_2$eyetr_value and gd_df_2$motr_value
t = 18, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6220 0.7305
sample estimates:
cor
0.68
gd_df_3 = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
cor.test(gd_df_3$eyetr_value, gd_df_3$motr_value)
Pearson's product-moment correlation
data: gd_df_3$eyetr_value and gd_df_3$motr_value
t = 19, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6406 0.7447
sample estimates:
cor
0.6963
gd_df_4 = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
cor.test(gd_df_4$eyetr_value, gd_df_4$motr_value)
Pearson's product-moment correlation
data: gd_df_4$eyetr_value and gd_df_4$motr_value
t = 14, df = 368, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.5197 0.6530
sample estimates:
cor
0.5903
provo_df %>%
gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
filter(metric == "gaze_duration") %>%
ggplot(aes(x = value, y=word_prop_val, color = measure)) +
geom_point(alpha = 0.2) +
facet_grid(word_prop~measure, scales="free") +
geom_smooth() +
xlab("Reading Measure")

# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
ggplot(aes(x = value, y=freq, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

provo_df %>%
ggplot(aes(x = value, y=surp, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

Shape of surprisal / RT relationship
fit_gam_inner = function(bootstrap_sample, mean_predictors) {
df = bootstrap_sample$data
weights = tabulate(as.integer(bootstrap_sample), nrow(df))
m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
terms_to_predict = c("s(surp)", "s(prev_surp)")
newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
len=mean_predictors$freq, prev_len=mean_predictors$freq)
# Returns a matrix N_samples * N_terms.
per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")
# Additive model -- sum across predictor response contributions (matrix columns).
predictions = rowSums(per_term_predictions)
return(newdata %>% mutate(y=predictions))
}
fit_gam = function(df, mean_predictors, alpha=0.05) {
# Bootstrap-resample data
boot_models = df %>% bootstraps(times=10) %>%
# Fit a GAM and get predictions for each sample
mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
# Extract mean and 5% and 95% percentile y-values for each surprisal value
result = boot_models %>%
unnest(smoothed) %>%
dplyr::select(surp, y) %>%
group_by(surp) %>%
summarise(y_lower=quantile(y, alpha / 2),
y_upper=quantile(y, 1 - alpha / 2),
y=mean(y)) %>%
ungroup()
return (result)
}
gam_modeling_df = provo_df %>%
spread(measure, value) %>%
mutate(len = nchar(word)) %>%
group_by(metric, text_id) %>%
arrange(word_text_idx) %>%
mutate(prev_surp = lag(surp),
prev_freq = lag(freq),
prev_len = lag(len),
prev_eyetr_value = lag(eyetr_value)) %>%
ungroup() %>%
drop_na() %>%
rename(psychometric = motr_value)
smooths_df = data.frame()
metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
print(paste0("Fitting model for ", m))
dummy_df = gam_modeling_df %>% filter(metric == m)
mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
smooths = dummy_df %>% fit_gam(., mean_predictors)
#Fix 0 surprisal = 0 ms
gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"
Targeted Evaluation Data
# View(motr_df)
motr_attach_df = motr_df %>%
filter(expr_id == "Attachment") %>%
rename( item_id = para_nr) %>%
mutate(item_id = as.integer(item_id)) %>%
mutate(cond_id = as.factor(cond_id)) %>%
mutate(cond_id = if_else(cond_id == 1, "No Comma",
if_else(cond_id == 2, "Comma",
if_else(cond_id == 3, "adv_high",
if_else(cond_id == 4, "adv_low",
if_else(cond_id == 5, "relative_high",
if_else(cond_id == 6, "relative_low",
if_else(cond_id == 7, "practice", "filler")))))))) %>%
filter(metric != "FPReg") %>%
filter(cond_id == "relative_high" | cond_id == "relative_low") %>%
# filter(! (item_id == 4 & cond_id == "No Comma") ) %>% # just because of alignment issues for now
mutate(crit = if_else(word == "who", word_nr, as.integer(0) )) %>%
group_by(cond_id, item_id) %>%
mutate(crit = unique(crit)[2]) %>%
ungroup() %>%
mutate(word_nr = word_nr - crit)
View(motr_attach_df)
agg_motr_attach_df = motr_attach_df %>%
drop_na() %>%
filter(word_nr >= -2, word_nr < 6) %>%
group_by(cond_id, word_nr, metric) %>%
summarise( m = mean(value),
sd = std.error(value),
upper = m + 1.96 * sd,
lower = m - 1.98 * sd,
n = n()) %>%
ungroup()
`summarise()` has grouped output by 'cond_id', 'word_nr'. You can override using the `.groups` argument.
View(agg_motr_attach_df)
agg_motr_attach_df %>%
ggplot(aes(x = word_nr, y = m, color = cond_id)) +
geom_rect(aes(xmin = 2.5, xmax = 5.5, ymin = 100, ymax = 800), fill=alpha("white", 0), color = "#45ef70", linetype = "dotted") +
geom_point() +
geom_errorbar(aes(ymax = upper, ymin = lower), width = 0.3) +
geom_line() +
#geom_text(aes(label = word, y = if_else(cond_id == "Comma", 3000, 3500)), size = 2) +
#facet_grid(para_nr~cond_id) +
ylab("Reading Time") +
xlab("Condition") +
# scale_x_continuous(breaks=-2:5, labels=c("the", "man", "and", "his", "wife", "ran", "away", "from")) +
scale_x_continuous(breaks=-2:5, labels=c('the', 'queen', 'who', 'praised', 'herself', 'all', 'the', 'time')) +
facet_grid(~metric) +
theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1)
)

#ggsave("../visualization/attachment.png", device = "png", width = 6, height = 3)
options(JULIA_HOME = "/Applications/Julia-1.8.app/Contents/Resources/julia/bin/")
# library(jglmm)
# jglmm_setup()
attach_lm_df = motr_attach_df %>%
filter(metric == "gaze_duration") %>%
filter(word_nr == 3) %>%
mutate(item_id = as.factor(item_id),
subj = as.factor(subj))
m = attach_lm_df %>%
lmer(value ~ cond_id + (cond_id | item_id) + (cond_id | subj), data=.)
boundary (singular) fit: see help('isSingular')
summary(m)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: value ~ cond_id + (cond_id | item_id) + (cond_id | subj)
Data: .
REML criterion at convergence: 1112
Scaled residuals:
Min 1Q Median 3Q Max
-1.797 -0.639 -0.280 0.523 3.497
Random effects:
Groups Name Variance Std.Dev. Corr
item_id (Intercept) 25837 160.7
cond_idrelative_low 2360 48.6 -1.00
subj (Intercept) 948 30.8
cond_idrelative_low 14451 120.2 1.00
Residual 63112 251.2
Number of obs: 80, groups: item_id, 24; subj, 5
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 252.1 53.8 15.5 4.68 0.00027 ***
cond_idrelative_low -18.4 79.1 5.1 -0.23 0.82560
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Correlation of Fixed Effects:
(Intr)
cnd_drltv_l -0.290
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzYCBpcyBhIGZ1bmN0aW9uIGluIFIgdGhhdCBzdXBwcmVzc2VzIHRoZSBzdGFydHVwIG1lc3NhZ2VzIHRoYXQgYXJlIGdlbmVyYXRlZCB3aGVuIGxvYWRpbmcgYSBwYWNrYWdlLiBJdCBjYW4gYmUgdXNlZnVsIHRvIHByZXZlbnQgdGhlIGNvbnNvbGUgZnJvbSBiZWluZyBjbHV0dGVyZWQgd2l0aCBtZXNzYWdlcyB3aGVuIHlvdSBhcmUgbG9hZGluZyBtdWx0aXBsZSBwYWNrYWdlcyBvciB3aGVuIHlvdSBkb24ndCB3YW50IHRvIHNlZSB0aGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzLgpzaGhoIDwtIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyAjIEl0J3MgYSBsaWJyYXJ5LCBzbyBzaGhoIQoKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKIyBgdGhlbWVfc2V0KHRoZW1lX2J3KCkpYCBpcyBhIGNvbW1hbmQgaW4gUiB0aGF0IHNldHMgdGhlIGRlZmF1bHQgdGhlbWUgZm9yIGFsbCBzdWJzZXF1ZW50IHBsb3RzIHRvIGEgdGhlbWUgY2FsbGVkIGB0aGVtZV9idygpYC4KIyBgdGhlbWVfYncoKWAgaXMgYSBidWlsdC1pbiB0aGVtZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgYSBjbGFzc2ljIGJsYWNrLWFuZC13aGl0ZSB0aGVtZSB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIEJ5IHNldHRpbmcgdGhlIGRlZmF1bHQgdGhlbWUgdG8gYHRoZW1lX2J3KClgLCBhbGwgcGxvdHMgdGhhdCBhcmUgY3JlYXRlZCB1c2luZyBnZ3Bsb3QyIHdpbGwgaGF2ZSB0aGlzIHRoZW1lIHVubGVzcyBhIGRpZmZlcmVudCB0aGVtZSBpcyBleHBsaWNpdGx5IHNwZWNpZmllZC4KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGBvcHRpb25zKGRpZ2l0cz00KWAgaXMgYSBjb21tYW5kIGluIFIgdGhhdCBzZXRzIHRoZSBudW1iZXIgb2YgZGlnaXRzIHRvIGRpc3BsYXkgd2hlbiBwcmludGluZyBudW1lcmljIHZhbHVlcy4KCiMgQnkgZGVmYXVsdCwgUiB3aWxsIHByaW50IHVwIHRvIDcgZGlnaXRzIGZvciBudW1lcmljIHZhbHVlcy4gSG93ZXZlciwgeW91IGNhbiB1c2UgdGhlIGBvcHRpb25zKClgIGZ1bmN0aW9uIHRvIGNoYW5nZSB0aGlzIGJlaGF2aW9yLiBJbiB0aGlzIGNhc2UsIGBvcHRpb25zKGRpZ2l0cz00KWAgc2V0cyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyB0byA0LCB3aGljaCBtZWFucyB0aGF0IGFsbCBudW1lcmljIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZCB3aXRoIGF0IG1vc3QgNCBkaWdpdHMuCm9wdGlvbnMoZGlnaXRzPTQpCnNldC5zZWVkKDQ0NCkKCiMgVGhlIGBwaXBlX21lc3NhZ2VgIGZ1bmN0aW9uIGZpcnN0IG91dHB1dHMgdGhlIHN0YXR1cyBtZXNzYWdlIHVzaW5nIHRoZSBtZXNzYWdlKCkgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgdHJhY2tpbmcgdGhlIHByb2dyZXNzIG9mIGEgbG9uZyBjb21wdXRhdGlvbiBvciBmb3IgcHJvdmlkaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB1c2VyLgojIEFmdGVyIHByaW50aW5nIHRoZSBtZXNzYWdlLCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgLmRhdGEgYXJndW1lbnQgdW5jaGFuZ2VkLiBUaGlzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCBhcyBhICJwaXBlIiBpbiBhIGRhdGEgcHJvY2Vzc2luZyBwaXBlbGluZSwgd2hlcmUgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gaXMgdXNlZCBhcyB0aGUgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uCnBpcGVfbWVzc2FnZSA9IGZ1bmN0aW9uKC5kYXRhLCBzdGF0dXMpIHttZXNzYWdlKHN0YXR1cyk7IC5kYXRhfQoKYGBgCgpgYGB7cn0KCmBgYAoKIyBSZWFkIGluIE1vVFIgRGF0YQoKYGBge3J9CiMgSW50ZXJlc3Rpbmcgd2F5IG9mIHJlYWRpbmcgbXVsdGlwbGUgZmlsZXMgaW4gb25lIGRpcmVjdG9yeSEKCmZpbGVfcHJlZml4ID0gIi4uL3JlYWRpbmdfbWVhc3VyZXMvY2xlYW5lZF9mMTYwIgpmbmFtZXMgPSBsaXN0LmZpbGVzKHBhdGg9ZmlsZV9wcmVmaXgpCgojIGZuYW1lczogWzFdICJyZWFkZXJfNTVfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNTZfcmVhZGluZ19tZWFzdXJlcy5jc3YiCiMgWzNdICJyZWFkZXJfNTlfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNjFfcmVhZGluZ19tZWFzdXJlcy5jc3YiCgojIFJlYWQgaW4gdGhlIGRhdGEKZGYgPSBkYXRhLmZyYW1lKCkKCiMgdXNlcyB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGluIGEgZGF0YSBmcmFtZSBjYWxsZWQgc3Viai4gVGhlIG5ldyBjb2x1bW4gaXMgY3JlYXRlZCBieSByZW1vdmluZyB0aGUgc3Vic3RyaW5nIF9yZWFkaW5nX21lYXN1cmVzLmNzdiBmcm9tIHRoZSB2YWx1ZXMgaW4gYW4gZXhpc3RpbmcgY29sdW1uIGNhbGxlZCBmLgpmb3IgKGYgaW4gZm5hbWVzKSB7CiAgdGVtcCA9IHJlYWQuY3N2KHBhc3RlMChmaWxlX3ByZWZpeCwgIi8iLCBmKSkgJT4lCiAgICBtdXRhdGUoc3ViaiA9IHN0cl9yZW1vdmUoZiwgIl9yZWFkaW5nX21lYXN1cmVzLmNzdiIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgZmlyc3RfZHVyYXRpb24sIHRvdGFsX2R1cmF0aW9uLAogICAgICAgICAgICAgICAgICBnYXplX2R1cmF0aW9uLCBnb19wYXNzX3RpbWUsIEZQUmVnLCBzdWJqKSAlPiUKICAgIHJlbmFtZShnb19wYXN0X3RpbWUgPSBnb19wYXNzX3RpbWUpCiAgZGYgPSByYmluZChkZiwgdGVtcCkKfQpkZgoKIyBgZ2F0aGVyIHdpbGwgdGFrZSBjb2x1bW4gNi05J3MgY29sdW1uIG5hbWUgbWFrZSBpdCByb3dzLCBuYW1lICdtZXRyaWMnLCB0YWtlIHRoZWlyIHZhbHVlIGFzIGFub3RoZXIgcm93YAptb3RyX2RmID0gZGYgJT4lCiAgbXV0YXRlKGV4cHJfaWQgPSBpZl9lbHNlKGV4cHJfaWQgPT0gMSwgIkF0dGFjaG1lbnQiLCAiUHJvdm8iKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIDY6MTApCgptb3RyX2RmCgpgYGAKCmBgYHtyfQojIEF2ZXJhZ2UgYWNyb3NzIHN1YmplY3RzCm1vdHJfYWdnX2RmID0gbW90cl9kZiAlPiUKICBkcm9wX25hKCkgJT4lCiAgZ3JvdXBfYnkoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgbWV0cmljKSAlPiUKICAgIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpKSAlPiUgICAgIyBzdW0gdXAgZm91ciBzdWJqZWN0cyBmb3IgZWFjaCBtZXRyaWMgYW5kIGRpdmlkZSBieSA0LgogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGV4cHJfaWQsIGNvbmRfaWQsIHBhcmFfbnIsIHdvcmRfbnIpICAgIyBsaWtlIHNvcnQgaW4gcHl0aG9uCgojIFZpZXcobW90cl9hZ2dfZGYpCgptb3RyX2FnZ19kZgoKdGFibGUobW90cl9kZiRzdWJqKQojIHJlYWRlcl81NSByZWFkZXJfNTYgcmVhZGVyXzU5IHJlYWRlcl82MSAKIyAgICAzMTAwICAgICAgMzEwMCAgICAgIDMxMDAgICAgICAzMTAwCgpgYGAKCmBgYHtyfQoKbW90cl9wcm92b19kZiA9IG1vdHJfYWdnX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJQcm92byIpICU+JQogIHJlbmFtZSh0ZXh0X2lkID0gcGFyYV9uciwKICAgICAgICAgd29yZF90ZXh0X2lkeCA9IHdvcmRfbnIsCiAgICAgICAgIG1vdHJfdmFsdWUgPSB2YWx1ZSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZXhwcl9pZCwgLWNvbmRfaWQpCgptb3RyX3Byb3ZvX2RmCgpgYGAKCgoKIyBDb21wYXJpc29uIHRvIFByb3ZvCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIHN1cnByaXNhbCwgZnJlcXVlbmN5IGFuZCBsZW5ndGggZGF0YQpwcm92b19tb2RlbGluZ19kZiA9IHJlYWQuY3N2KCIuLi9hbmNpbGxhcnlfZGF0YS9wcm92b19kZi5jc3YiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHRleHRfaWQsIHNlbnRfaWQsIHRyaWdnZXJfaWR4LCB3b3JkLCBmcmVxLCBzdXJwLCBsZW4pICU+JQogIHJlbmFtZSh3b3JkX2lkeCA9IHRyaWdnZXJfaWR4KQoKcHJvdm9fbW9kZWxpbmdfZGYKCmBgYAoKYGBge3J9CiMgUmVhZCBpbiBQcm92byBleWV0cmFja2luZyBkYXRhCgpwcm92b19yYXdfZGYgPSByZWFkLmNzdigiLi4vYW5jaWxsYXJ5X2RhdGEvcHJvdm9fZXlldHJhY2tpbmcuY3N2IikKCnByb3ZvX2V5ZXRyYWNraW5nX2RmID0gcHJvdm9fcmF3X2RmICU+JQogIGRwbHlyOjpzZWxlY3QoUGFydGljaXBhbnRfSUQsIFRleHRfSUQsIFNlbnRlbmNlX051bWJlciwgV29yZF9Jbl9TZW50ZW5jZV9OdW1iZXIsIElBX0lELCBXb3JkLElBX0ZJUlNUX0ZJWEFUSU9OX0RVUkFUSU9OLCBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUsIElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLCBJQV9EV0VMTF9USU1FLCBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sIElBX1JFR1JFU1NJT05fT1VUKSAlPiUKICByZW5hbWUoIGZpcnN0X2R1cmF0aW9uID0gSUFfRklSU1RfRklYQVRJT05fRFVSQVRJT04sICAgICMgd2hldGhlciBpdCBpcyBmaXJzdCBwYXNzPwogICAgICAgICAgZ2F6ZV9kdXJhdGlvbiA9IElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLAogICAgICAgICAgdG90YWxfZHVyYXRpb24gPSBJQV9EV0VMTF9USU1FLAogICAgICAgICAgZ29fcGFzdF90aW1lID0gSUFfUkVHUkVTU0lPTl9QQVRIX0RVUkFUSU9OLAogICAgICAgICAgRlBSZWcgPSBJQV9SRUdSRVNTSU9OX09VVCwKICAgICAgICAgIHN1YmogPSBQYXJ0aWNpcGFudF9JRCwKICAgICAgICAgIHRleHRfaWQgPSBUZXh0X0lELAogICAgICAgICAgc2VudF9pZCA9IFNlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfaWR4ID0gV29yZF9Jbl9TZW50ZW5jZV9OdW1iZXIsCiAgICAgICAgICB3b3JkX3RleHRfaWR4ID0gSUFfSUQsCiAgICAgICAgICB3b3JkID0gV29yZCwKICAgICAgICAgIGZmX3Byb2dyZXNzaXZlID0gSUFfRklSU1RfRklYX1BST0dSRVNTSVZFKSAlPiUgIyBub3RpY2U6YXZlcmFnZSBhY3Jvc3Mgc3ViaiwgYmluYXJ5KDAsMSkgYmVjb21lcyBmbG9hdC4KICBtdXRhdGUoZ2F6ZV9kdXJhdGlvbiA9IGlmZWxzZShmZl9wcm9ncmVzc2l2ZSA9PSAwLCAwLCBnYXplX2R1cmF0aW9uKSwKICAgICAgICAgZ29fcGFzdF90aW1lID0gaWZlbHNlKGZmX3Byb2dyZXNzaXZlID09IDAsIDAsIGdvX3Bhc3RfdGltZSkpICU+JQogIGRwbHlyOjpzZWxlY3QoLWZmX3Byb2dyZXNzaXZlKSAlPiUKICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgNzoxMSkgJT4lICAgICAgIyBub3QgaW5jbHVkZSBGRlJlZyB3aGljaCBpcyBpbiBjb2x1bW4gMTEKICBtdXRhdGUodmFsdWUgPSBpZl9lbHNlKGlzLm5hKHZhbHVlKSwgYXMuaW50ZWdlcigwKSwgYXMuaW50ZWdlcih2YWx1ZSkpKSAlPiUKICBkcm9wX25hKCkgJT4lICAgIyBhY3R1YWxseSwgZHJvcCBmaXJzdCB3b3JkIGluIGEgc2VudGVuY2UKICBncm91cF9ieSh0ZXh0X2lkLCB3b3JkX3RleHRfaWR4LCBzZW50X2lkLCB3b3JkX2lkeCwgd29yZCwgbWV0cmljKSAlPiUKICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKHZhbHVlKSkgJT4lCiAgdW5ncm91cCgpCgojIFZpZXcocHJvdm9fZXlldHJhY2tpbmdfZGYpCnByb3ZvX2V5ZXRyYWNraW5nX2RmCgpgYGAKCgpgYGB7cn0KcHJvdm9fZGYgPSBtZXJnZShwcm92b19leWV0cmFja2luZ19kZiwgcHJvdm9fbW9kZWxpbmdfZGYsIGJ5PWMoInRleHRfaWQiLCAic2VudF9pZCIsICJ3b3JkX2lkeCIpKSAlPiUKICBtdXRhdGUod29yZF90ZXh0X2lkeCA9IGFzLmludGVnZXIod29yZF90ZXh0X2lkeCAtIDEpKSAlPiUKICBhcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KQpwcm92b19kZgoKcHJvdm9fZGYgPSBtZXJnZShwcm92b19kZiwgbW90cl9wcm92b19kZiwgYnk9YygidGV4dF9pZCIsICJ3b3JkX3RleHRfaWR4IiwgIm1ldHJpYyIpKSAlPiUKICByZW5hbWUoZXlldHJfdmFsdWUgPSB2YWx1ZSkgJT4lCiAgYXJyYW5nZSh0ZXh0X2lkLCBzZW50X2lkLCB3b3JkX2lkeCkgJT4lCiAgZmlsdGVyKHdvcmQueCA9PSB3b3JkKSAlPiUgICAgICAjd29yZC55IGhhcyBubyBjYXB0aWNhbCB3b3JkCiAgZHBseXI6OnNlbGVjdCgtd29yZC54LCAtd29yZC55KSAlPiUKICBtdXRhdGUobW90cl9vdXRsaWVyID0gaWZfZWxzZShtb3RyX3ZhbHVlID4gKG1lYW4obW90cl92YWx1ZSkgKyAzICogc2QobW90cl92YWx1ZSkgKSwgVCwgRikpICU+JQogIGZpbHRlcihtb3RyX291dGxpZXIgPT0gRikgJT4lICAgICAjIGNsZWFyIG91dGxpZXIgLT4gMTMgd2FzIGZpbHRlcmVkLgogIGdhdGhlcihtZWFzdXJlLCB2YWx1ZSwgYygiZXlldHJfdmFsdWUiLCAibW90cl92YWx1ZSIpKSAgJT4lCiAgIyBmaWx0ZXIobWV0cmljICE9ICJmaXJzdF9kdXJhdGlvbiIpICU+JQogIGZpbHRlcihtZXRyaWMgIT0gIkZQUmVnIikKCiMgVmlldyhwcm92b19kZikKCgpgYGAKCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGZhY2V0X3dyYXAoLn5tZWFzdXJlKSArCiAgICB4bGFiKCJSZWFkaW5nIE1lYXN1cmUiKQojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9kZW5zaXR5LnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0gIm1vdHJfdmFsdWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogICAgZ2d0aXRsZSgiRGVuc2l0eSBwbG90IG9mIHZhbHVlIGZvciBtb3RyIikKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0gImV5ZXRyX3ZhbHVlIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpICsKICAgIGdndGl0bGUoIkRlbnNpdHkgcGxvdCBvZiB2YWx1ZSBmb3IgZXlldHIiKQpgYGAKCgpgYGB7cn0KZ2RfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAidG90YWxfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQojIFZpZXcoZ2RfZGYpCgpjb3IudGVzdChnZF9kZiRleWV0cl92YWx1ZSwgZ2RfZGYkbW90cl92YWx1ZSkgICAgICAgICAgI2NvciByaXNlIGZyb20gMC41MjkgdG8gMC41OTg5IAoKcHJvdm9fZGYgJT4lCiAgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtb3RyX3ZhbHVlLCB5PWV5ZXRyX3ZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF93cmFwKC5+bWV0cmljLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9tZXRyaWNfY29yLnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKCmBgYAoKYGBge3J9CmdkX2RmXzIgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMiRleWV0cl92YWx1ZSwgZ2RfZGZfMiRtb3RyX3ZhbHVlKQpgYGAKCmBgYHtyfQpnZF9kZl8zID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMyRleWV0cl92YWx1ZSwgZ2RfZGZfMyRtb3RyX3ZhbHVlKQpgYGAKCgpgYGB7cn0KZ2RfZGZfNCA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnb19wYXN0X3RpbWUiKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfNCRleWV0cl92YWx1ZSwgZ2RfZGZfNCRtb3RyX3ZhbHVlKQoKYGBgCmBgYHtyfQpwcm92b19kZiAlPiUKICBnYXRoZXIod29yZF9wcm9wLCB3b3JkX3Byb3BfdmFsLCBjKCJmcmVxIiwgImxlbiIsICJzdXJwIikpICU+JQogIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT13b3JkX3Byb3BfdmFsLCBjb2xvciA9IG1lYXN1cmUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKHdvcmRfcHJvcH5tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi93b3JkX3Byb3BfY29tcHMucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMykKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHk9ZnJlcSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgZmFjZXRfZ3JpZChtZXRyaWN+bWVhc3VyZSwgc2NhbGVzPSJmcmVlIikgKwogICAgZ2VvbV9zbW9vdGgoKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1zdXJwLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKCgojIyBTaGFwZSBvZiBzdXJwcmlzYWwgLyBSVCByZWxhdGlvbnNoaXAKCmBgYHtyfQoKZml0X2dhbV9pbm5lciA9IGZ1bmN0aW9uKGJvb3RzdHJhcF9zYW1wbGUsIG1lYW5fcHJlZGljdG9ycykgewogIAogIGRmID0gYm9vdHN0cmFwX3NhbXBsZSRkYXRhCiAgd2VpZ2h0cyA9IHRhYnVsYXRlKGFzLmludGVnZXIoYm9vdHN0cmFwX3NhbXBsZSksIG5yb3coZGYpKQogIAogIG0gPSBnYW0ocHN5Y2hvbWV0cmljIH4gcyhzdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHMocHJldl9zdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHRlKGZyZXEsIGxlbiwgYnMgPSAnY3InKSArIHRlKHByZXZfZnJlcSwgcHJldl9sZW4sIGJzID0gJ2NyJyksIGRhdGEgPSBkZiwgd2VpZ2h0cyA9IHdlaWdodHMpCiAgdGVybXNfdG9fcHJlZGljdCA9IGMoInMoc3VycCkiLCAicyhwcmV2X3N1cnApIikKICAKICBuZXdkYXRhID0gZGF0YS5mcmFtZShzdXJwPXNlcSgwLDIwLGJ5PTAuMSksIHByZXZfc3VycD1tZWFuX3ByZWRpY3RvcnMkc3VycCwKICAgICAgICAgICAgICAgICAgICAgICBmcmVxPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2ZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgbGVuPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2xlbj1tZWFuX3ByZWRpY3RvcnMkZnJlcSkKCiAgIyBSZXR1cm5zIGEgbWF0cml4IE5fc2FtcGxlcyAqIE5fdGVybXMuCiAgcGVyX3Rlcm1fcHJlZGljdGlvbnMgPSBwcmVkaWN0KG0sIG5ld2RhdGE9bmV3ZGF0YSwgdGVybXM9dGVybXNfdG9fcHJlZGljdCwgdHlwZT0idGVybXMiKQoKICAjIEFkZGl0aXZlIG1vZGVsIC0tIHN1bSBhY3Jvc3MgcHJlZGljdG9yIHJlc3BvbnNlIGNvbnRyaWJ1dGlvbnMgKG1hdHJpeCBjb2x1bW5zKS4KICBwcmVkaWN0aW9ucyA9IHJvd1N1bXMocGVyX3Rlcm1fcHJlZGljdGlvbnMpCgogIHJldHVybihuZXdkYXRhICU+JSBtdXRhdGUoeT1wcmVkaWN0aW9ucykpCn0KCmZpdF9nYW0gPSBmdW5jdGlvbihkZiwgbWVhbl9wcmVkaWN0b3JzLCBhbHBoYT0wLjA1KSB7CiAgIyBCb290c3RyYXAtcmVzYW1wbGUgZGF0YQogIGJvb3RfbW9kZWxzID0gZGYgJT4lIGJvb3RzdHJhcHModGltZXM9MTApICU+JSAKICAgIyBGaXQgYSBHQU0gYW5kIGdldCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzYW1wbGUKICAgIG11dGF0ZShzbW9vdGhlZD1tYXAoc3BsaXRzLCBmaXRfZ2FtX2lubmVyLCBtZWFuX3ByZWRpY3RvcnM9bWVhbl9wcmVkaWN0b3JzKSkKICAKICAjIEV4dHJhY3QgbWVhbiBhbmQgNSUgYW5kIDk1JSBwZXJjZW50aWxlIHktdmFsdWVzIGZvciBlYWNoIHN1cnByaXNhbCB2YWx1ZQogIHJlc3VsdCA9IGJvb3RfbW9kZWxzICU+JSAKICAgIHVubmVzdChzbW9vdGhlZCkgJT4lIAogICAgZHBseXI6OnNlbGVjdChzdXJwLCB5KSAlPiUgCiAgICBncm91cF9ieShzdXJwKSAlPiUgCiAgICAgIHN1bW1hcmlzZSh5X2xvd2VyPXF1YW50aWxlKHksIGFscGhhIC8gMiksIAogICAgICAgICAgICAgICAgeV91cHBlcj1xdWFudGlsZSh5LCAxIC0gYWxwaGEgLyAyKSwKICAgICAgICAgICAgICAgIHk9bWVhbih5KSkgJT4lIAogICAgdW5ncm91cCgpCiAgCiAgcmV0dXJuIChyZXN1bHQpCn0KCgpgYGAKCgpgYGB7cn0KCmdhbV9tb2RlbGluZ19kZiA9IHByb3ZvX2RmICU+JQogIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkgJT4lCiAgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUKICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCgoKc21vb3Roc19kZiA9IGRhdGEuZnJhbWUoKQoKbWV0cmljcyA9IGMoImdhemVfZHVyYXRpb24iLCAidG90YWxfZHVyYXRpb24iLCAiZ29fcGFzdF90aW1lIiwgImZpcnN0X2R1cmF0aW9uIikKZm9yIChtIGluIG1ldHJpY3MpIHsKICBwcmludChwYXN0ZTAoIkZpdHRpbmcgbW9kZWwgZm9yICIsIG0pKQogIGR1bW15X2RmID0gZ2FtX21vZGVsaW5nX2RmICU+JSBmaWx0ZXIobWV0cmljID09IG0pCiAgbWVhbl9wcmVkaWN0b3JzID0gZHVtbXlfZGYgJT4lIHN1bW1hcmlzZShzdXJwID0gbWVhbihzdXJwKSwgbGVuID0gbWVhbihsZW4pLCBmcmVxID0gbWVhbihmcmVxKSkKICBzbW9vdGhzID0gZHVtbXlfZGYgJT4lIGZpdF9nYW0oLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCn0KCmBgYAoKCmBgYHtyfQojIFN1cnByaXNhbCBjdXJ2ZXMKICBnZ3Bsb3QoKSArCiAgICAgIGdlb21fbGluZShkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9c3VycCwgeT15LCBjb2xvciA9IHBzeWNob21ldHJpYyksIHNpemU9MC43KSArCiAgICAgIGdlb21fcmliYm9uKGRhdGEgPSBzbW9vdGhzX2RmLCBhZXMoeD1zdXJwLCB5bWluPXlfbG93ZXIsIHltYXg9eV91cHBlciwgZmlsbCA9IHBzeWNob21ldHJpYyksIGFscGhhPTAuMywgc2l6ZT0wLjUpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1jKDAsIDEwLCAyMCksIGJyZWFrcz1jKDAsIDEwLCAyMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsKICAgICAgZmFjZXRfd3JhcChwc3ljaG9tZXRyaWN+LikgKwogICAgICB5bGFiKCJTbG93ZG93biBkdWUgdG8gU3VycHJpc2FsIChtcykiKSArCiAgICAgIHhsYWIoIlN1cnByaXNhbCBvZiBXb3JkIikgKwogICAgICBnZ3RpdGxlKCJSZWxhdGlvbnNoaXAgYmV0d2VlbiBNb1RSIFRpbWVzIGFuZCBTdXJwcmlzYWwiKQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgoKCgoKCmBgYHtyfQoKCgoKCmBgYAoKIyBUYXJnZXRlZCBFdmFsdWF0aW9uIERhdGEKCgoKYGBge3J9CiMgVmlldyhtb3RyX2RmKQoKbW90cl9hdHRhY2hfZGYgPSBtb3RyX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJBdHRhY2htZW50IikgJT4lCiAgcmVuYW1lKCBpdGVtX2lkID0gcGFyYV9ucikgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5pbnRlZ2VyKGl0ZW1faWQpKSAlPiUKICBtdXRhdGUoY29uZF9pZCA9IGFzLmZhY3Rvcihjb25kX2lkKSkgJT4lCiAgbXV0YXRlKGNvbmRfaWQgPSBpZl9lbHNlKGNvbmRfaWQgPT0gMSwgIk5vIENvbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDIsICJDb21tYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDMsICJhZHZfaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoY29uZF9pZCA9PSA0LCAiYWR2X2xvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDUsICJyZWxhdGl2ZV9oaWdoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoY29uZF9pZCA9PSA2LCAicmVsYXRpdmVfbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDcsICJwcmFjdGljZSIsICJmaWxsZXIiKSkpKSkpKSkgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKSAlPiUKICBmaWx0ZXIoY29uZF9pZCA9PSAicmVsYXRpdmVfaGlnaCIgfCBjb25kX2lkID09ICJyZWxhdGl2ZV9sb3ciKSAlPiUKCiAgIyBmaWx0ZXIoISAoaXRlbV9pZCA9PSA0ICYgY29uZF9pZCA9PSAiTm8gQ29tbWEiKSApICU+JSAjIGp1c3QgYmVjYXVzZSBvZiBhbGlnbm1lbnQgaXNzdWVzIGZvciBub3cKICAKICBtdXRhdGUoY3JpdCA9IGlmX2Vsc2Uod29yZCA9PSAid2hvIiwgd29yZF9uciwgYXMuaW50ZWdlcigwKSApKSAlPiUKICBncm91cF9ieShjb25kX2lkLCBpdGVtX2lkKSAlPiUKICAgIG11dGF0ZShjcml0ID0gdW5pcXVlKGNyaXQpWzJdKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmRfbnIgPSB3b3JkX25yIC0gY3JpdCkKVmlldyhtb3RyX2F0dGFjaF9kZikKCgphZ2dfbW90cl9hdHRhY2hfZGYgPSBtb3RyX2F0dGFjaF9kZiAlPiUKICBkcm9wX25hKCkgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPj0gLTIsIHdvcmRfbnIgPCA2KSAlPiUKICBncm91cF9ieShjb25kX2lkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKCBtID0gbWVhbih2YWx1ZSksCiAgICAgICAgICAgICAgIHNkID0gc3RkLmVycm9yKHZhbHVlKSwKICAgICAgICAgICAgICAgdXBwZXIgPSBtICsgMS45NiAqIHNkLAogICAgICAgICAgICAgICBsb3dlciA9IG0gLSAxLjk4ICogc2QsCiAgICAgICAgICAgICAgIG4gPSBuKCkpICU+JQogIHVuZ3JvdXAoKQoKVmlldyhhZ2dfbW90cl9hdHRhY2hfZGYpCgphZ2dfbW90cl9hdHRhY2hfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd29yZF9uciwgeSA9IG0sIGNvbG9yID0gY29uZF9pZCkpICsKICAgIGdlb21fcmVjdChhZXMoeG1pbiA9IDIuNSwgeG1heCA9IDUuNSwgeW1pbiA9IDEwMCwgeW1heCA9IDgwMCksIGZpbGw9YWxwaGEoIndoaXRlIiwgMCksIGNvbG9yID0gIiM0NWVmNzAiLCBsaW5ldHlwZSA9ICJkb3R0ZWQiKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1heCA9IHVwcGVyLCB5bWluID0gbG93ZXIpLCB3aWR0aCA9IDAuMykgKwogICAgZ2VvbV9saW5lKCkgKwogICAgI2dlb21fdGV4dChhZXMobGFiZWwgPSB3b3JkLCB5ID0gaWZfZWxzZShjb25kX2lkID09ICJDb21tYSIsIDMwMDAsIDM1MDApKSwgc2l6ZSA9IDIpICsKICAgICNmYWNldF9ncmlkKHBhcmFfbnJ+Y29uZF9pZCkgKwogIHlsYWIoIlJlYWRpbmcgVGltZSIpICsKICB4bGFiKCJDb25kaXRpb24iKSArCiAgIyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCJ0aGUiLCAibWFuIiwgImFuZCIsICJoaXMiLCAid2lmZSIsICJyYW4iLCAiYXdheSIsICJmcm9tIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCd0aGUnLCAncXVlZW4nLCAnd2hvJywgJ3ByYWlzZWQnLCAnaGVyc2VsZicsICdhbGwnLCAndGhlJywgJ3RpbWUnKSkgKwogIGZhY2V0X2dyaWQofm1ldHJpYykgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpCiAgKQoKI2dnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9hdHRhY2htZW50LnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCgpgYGAKCmBgYHtyfQoKb3B0aW9ucyhKVUxJQV9IT01FID0gIi9BcHBsaWNhdGlvbnMvSnVsaWEtMS44LmFwcC9Db250ZW50cy9SZXNvdXJjZXMvanVsaWEvYmluLyIpCiMgbGlicmFyeShqZ2xtbSkKIyBqZ2xtbV9zZXR1cCgpCgphdHRhY2hfbG1fZGYgPSBtb3RyX2F0dGFjaF9kZiAlPiUKICBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPT0gMykgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5mYWN0b3IoaXRlbV9pZCksCiAgICAgICAgIHN1YmogPSBhcy5mYWN0b3Ioc3ViaikpCgptID0gYXR0YWNoX2xtX2RmICU+JQogIGxtZXIodmFsdWUgfiBjb25kX2lkICsgKGNvbmRfaWQgfCBpdGVtX2lkKSArIChjb25kX2lkIHwgc3ViaiksIGRhdGE9LikKCnN1bW1hcnkobSkKCmBgYAoKCgoK